package com.waves.task.strategy.receive;

import com.waves.core.constant.TimeConstant;
import com.waves.core.constant.redis.TaskRedisKey;
import com.waves.core.utils.AssertUtil;
import com.waves.core.utils.LoginUtils;
import com.waves.core.utils.RedisUtils;
import com.waves.core.web.domain.R;
import com.waves.task.domain.entity.Task;
import com.waves.task.domain.enums.ReceiveTypeEnum;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@Component
public class ReceiveLuaStrategy extends AbstractReceiveStrategy {


    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    ReceiveTypeEnum getReceiveTypeEnum() {
        return ReceiveTypeEnum.RECEIVE_LUA;
    }

    @Override
    public R<Object> receiveTask(Task task) {
        // 使用lua脚本进行库存扣减
        receiveTaskWithLua(task);
        return super.saveTaskReceive(task);
    }

    @Override
    public void cancelTask(Task task) {
        // 取消任务加1
        String key = TaskRedisKey.getKey(TaskRedisKey.TASK_RECEIVE_NUM, task.getId());
        stringRedisTemplate.opsForValue().increment(key);
    }

    @Override
    public void createTask(Task task) {
        // 创建任务
        String key = TaskRedisKey.getKey(TaskRedisKey.TASK_RECEIVE_NUM, task.getId());
        Integer num = task.getBountyNum() != null ? task.getBountyNum() : task.getHelpBountyNum();
        RedisUtils.set(key, num,TimeConstant.YEAR_TIME);

//        String key2 = TaskRedisKey.getKey(TaskRedisKey.TASK_RECEIVE_USER_LIST, task.getId());
//        RedisUtils.lSet(key2, task.getUserId(), TimeConstant.YEAR_TIME);
    }

    public void receiveTaskWithLua(Task task) {
        /*
            脚本流程如下：
            首先，通过 redis.call('get', KEYS[1]) 获取名为 KEYS[1] 的键对应的值，这里 KEYS[1] 是商品库存的键。
            然后，使用 tonumber() 函数将获取到的值转换成数字类型。
            接着，判断获取到的值是否大于0，如果大于0，表示商品库存充足，可以进行购买。
            如果商品库存充足，执行以下操作：
            使用 redis.call('decr', KEYS[1]) 命令将 KEYS[1] 对应的值减1，表示购买了一个商品。
            使用 redis.call('lpush', KEYS[2], ARGV[1]) 命令将 ARGV[1]（用户编号）添加到名为 KEYS[2] 的列表中，表示用户的操作记录。
            返回数字1，表示购买成功。
            如果商品库存不足（获取到的值不大于0），返回数字0，表示购买失败。
         */
        String script = "if tonumber(redis.call('get', KEYS[1])) > 0 then redis.call('decr', KEYS[1]) redis.call('lpush', KEYS[2], ARGV[1]) return 1 else return 0 end";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); //创建一个 DefaultRedisScript 对象，并设置 Lua 脚本的文本、结果类型等属性。
        redisScript.setScriptText(script);
        redisScript.setResultType(Long.class);
        // 创建一个 keyList 列表，用于传递给 Lua 脚本的参数，其中包含了商品库存的键和用户列表的键。
        List<String> keyList = new ArrayList<>();
        String key1 = TaskRedisKey.getKey(TaskRedisKey.TASK_RECEIVE_NUM, task.getId());
        String key2 = TaskRedisKey.getKey(TaskRedisKey.TASK_RECEIVE_USER_LIST, task.getId());
        keyList.add(key1); //KEYS[1] 代表商品的库存key
        keyList.add(key2);  //KEYS[2] 代表用户的列表key
        //ARGV[1] 代表用户的编号
        // 使用 redisTemplate.execute() 方法执行 Lua 脚本，传入 Lua 脚本对象、keyList 和用户随机编号作为参数，得到执行结果。
        Long result = stringRedisTemplate.execute(redisScript, keyList, LoginUtils.getUserId().toString());
        //对返回值结果的判断
        if (result == 0) {
            AssertUtil.isTrue(false, "任务赏金数量不足无法领取");
        }
    }
}
